package org.example.javafxdemo;

import fr.brouillard.oss.commonmark.ext.notifications.Notification;
import fr.brouillard.oss.commonmark.ext.notifications.NotificationsExtension;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.scene.control.TextArea;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.concurrent.Worker;
import javafx.concurrent.Task;
import java.io.File;
import java.io.FileWriter;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.commonmark.Extension;
import org.commonmark.ext.autolink.AutolinkExtension;
import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension;
import org.commonmark.ext.heading.anchor.IdGenerator;
import org.commonmark.ext.ins.InsExtension;
import org.commonmark.ext.task.list.items.TaskListItemsExtension;
import org.commonmark.node.*;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.NodeRenderer;
import org.commonmark.renderer.html.*;
import org.commonmark.renderer.markdown.MarkdownRenderer;


import org.commonmark.ext.gfm.tables.TablesExtension;

public class MarkdownNoteController {

    @FXML
    private TextArea markdownArea;

    @FXML
    private WebView previewArea;

    @FXML
    public void initialize() {
        previewArea.applyCss();
        previewArea.getEngine().loadContent(markdownToHtml(markdownArea.getText()));
        previewArea.getEngine().getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> {
            if (newState == Worker.State.SUCCEEDED) {
                previewArea.setPrefHeight((Integer) previewArea.getEngine().executeScript("document.body.scrollHeight"));
                if (newState == Worker.State.SUCCEEDED) {
                    // 使用 JavaScript 获取所有 p 标签的内容
                    String script = "var pContents = []; " +
                            "var pTags = document.getElementsByTagName('p'); " +
                            "for (var i = 0; i < pTags.length; i++) { " +
                            "    pContents.push(pTags[i].textContent); " +
                            "} " +
                            "pContents.join(' ');";
                    String pContents = (String) previewArea.getEngine().executeScript(script);

                    System.out.println("原始 p 标签内容: " + pContents);

                    // 使用 JavaScript 修改 p 标签的内容
                    String newContent = "这是修改后的内容";
                    script = "var pTags = document.getElementsByTagName('p'); " +
                            "for (var i = 0; i < pTags.length; i++) { " +
                            "    pTags[i].textContent = '" + newContent + "'; " +
                            "}";
                    previewArea.getEngine().executeScript(script);
                }
            }
        });
        // 添加用户样式
        previewArea.getEngine().setUserStyleSheetLocation(getClass().getResource("css/colour-messages.css").toExternalForm());
    }

    @FXML
    private void handleSave() {
        try (FileWriter writer = new FileWriter("note.md")) {
            writer.write(markdownArea.getText());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @FXML
    private void handleLoad() {
        try (BufferedReader reader = new BufferedReader(new FileReader("note.md"))) {
            StringBuilder content = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                content.append(line).append("\n");
            }
            markdownArea.setText(content.toString());
            previewArea.getEngine().loadContent(markdownToHtml(content.toString()));

            // 在页面加载完成后执行JavaScript
            previewArea.getEngine().getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> {
                if (newState == javafx.concurrent.Worker.State.SUCCEEDED) {
                    System.out.println("obs = " + obs);
                    System.out.println("oldState = " + oldState);
                    System.out.println("newState = " + newState);
                    moveCursorToEnd(previewArea.getEngine());
                }
            });

            //previewArea.getCssMetaData()
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void moveCursorToEnd(WebEngine webEngine) {
        System.out.println("moveCursorToEnd");
        // 使用JavaScript将光标移动到<h1>元素的末尾
        webEngine.executeScript("""
                function moveCursorToEnd() {
                    var h1 = document.querySelector('h1');
                    var range = document.createRange();
                    var sel = window.getSelection();
                    range.selectNodeContents(h1);
                    range.collapse(false);
                    sel.removeAllRanges();
                    sel.addRange(range);
                }
                moveCursorToEnd();
                """);
        System.out.println("moveCursorToEnd end");
    }
    private String markdownToHtml(String markdown) {
        //在某些元素上添加或更改属性：在渲染器上为AttributeProvider注册一个工厂，以在img元素上设置class="border"属性。
        //HtmlRenderer renderer = HtmlRenderer.builder()
        //        .attributeProviderFactory(new AttributeProviderFactory() {
        //            public AttributeProvider create(AttributeProviderContext context) {
        //                return new ImageAttributeProvider();
        //            }
        //        })
        //        .build();
        //markdown = "![text](/url.png)";

        // 完全控制超文本标记语言的呈现方式。
        // 将缩进代码块的呈现更改为仅将它们包装在pre而不是pre和code中：
        //HtmlRenderer renderer = HtmlRenderer.builder()
        //        .nodeRendererFactory(new HtmlNodeRendererFactory() {
        //            public NodeRenderer create(HtmlNodeRendererContext context) {
        //                return new IndentedCodeBlockNodeRenderer(context);
        //            }
        //        })
        //        .build();
        //markdown = "Example:\n\n    code";

        //在构建器上配置扩展：GitHub风味Markdown启用表

        Extension notificationExtension = NotificationsExtension.create()
                .withClassMapper(n -> n == Notification.ERROR ? "alert alert-danger" : "alert alert-" + n.name().toLowerCase());

        List<Extension> extensions = List.of(
                TablesExtension.create(),   // 表格
                AutolinkExtension.create(),   // 自动链接
                InsExtension.create(),   // 下划线
                TaskListItemsExtension.create(),   // 任务列表
                //NotificationsExtension.create(),   // 第三方扩展：通知/警告段落 https://github.com/McFoggy/commonmark-ext-notifications
                notificationExtension,
                StrikethroughExtension.create())    // 删除线
                ;
        Parser parser = Parser.builder()
                .extensions(extensions)
                .build();
        HtmlRenderer renderer = HtmlRenderer.builder()
                .extensions(extensions)
                .attributeProviderFactory(new AttributeProviderFactory() {
                    public AttributeProvider create(AttributeProviderContext context) {
                        return new ImageAttributeProvider();
                    }
                })
                .build();

        //HtmlRenderer renderer = HtmlRenderer.builder().build();
        //// 将 markdown 转换为 HTML
        //Parser parser = Parser.builder().build();
        Node document = parser.parse(markdown);

        String result = renderer.render(document);
        System.out.println("result = " + result);

        // 在渲染前进行修改，或者在不渲染的情况下进行检查：
        // 统计 markdown 中单词数
        WordCountVisitor visitor = new WordCountVisitor();
        document.accept(visitor);
        int wordCount = visitor.wordCount;
        System.out.println("wordCount" + wordCount);


        return result;

        // 以 H2 显示 markdown 文本
        //MarkdownRenderer renderer = MarkdownRenderer.builder().build();
        //Node document = new Document();
        //Heading heading = new Heading();
        //heading.setLevel(2);
        //heading.appendChild(new Text("My title"));
        //document.appendChild(heading);
        //return renderer.render(document);
    }
}

class WordCountVisitor extends AbstractVisitor {
    int wordCount = 0;
    @Override
    public void visit(Text text) {
        // This is called for all Text nodes. Override other visit methods for other node types.

        // Count words (this is just an example, don't actually do it this way for various reasons).
        wordCount += text.getLiteral().split("\\W+").length;

        //text.appendChild(text);
        // Descend into children (could be omitted in this case because Text nodes don't have children).
        visitChildren(text);
    }
}

class ImageAttributeProvider implements AttributeProvider {
    @Override
    public void setAttributes(Node node, String tagName, Map<String, String> attributes) {
        if (node instanceof Image) {
            //在img元素上设置class="border"属性。
            attributes.put("contenteditable", "true");
        } else {
            attributes.put("contenteditable", "true");
        }
    }
}

class IndentedCodeBlockNodeRenderer implements NodeRenderer {

    private final HtmlWriter html;

    IndentedCodeBlockNodeRenderer(HtmlNodeRendererContext context) {
        this.html = context.getWriter();
    }

    @Override
    public Set<Class<? extends Node>> getNodeTypes() {
        // Return the node types we want to use this renderer for.
        return Set.of(IndentedCodeBlock.class);
    }

    @Override
    public void render(Node node) {
        // We only handle one type as per getNodeTypes, so we can just cast it here.
        IndentedCodeBlock codeBlock = (IndentedCodeBlock) node;
        html.line();
        html.tag("pre");
        html.text(codeBlock.getLiteral());
        html.tag("/pre");
        html.line();
    }
}